package com.ruoyi.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;

import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;

/**
 * Apache Http客户端工具类
 */
@Slf4j
public class HttpClientUtil {

    private static final int REQUEST_TIMEOUT = 6000000;
    private static final int SOCKET_TIMEOUT = 6000000;
    private static final int CONNECT_TIMEOUT = 6000000;
    //Httpclient连接池，长连接保持30秒
    private static final int TIME_TO_LIVE = 30;
    //连接池最大连接数
    private static final int MAX_TOTAL = 400;
    //设置同路由的并发数
    private static final int MAX_PER_ROUTE = 100;
    //可用空闲连接过期时间
    private static final int ValidateAfterInactivity=100000;
    private static final String DEFAULT_CHARSET = "UTF-8";

 
    private static final String JSON_CONTENT_TYPE = "application/json";
 
    private static final int SUCCESS_STATUS = 200;
    private static final String HTTP = "http";
    private static final String HTTPS = "https";
    private static final Object syncLock = new Object();

    private static CloseableHttpClient HTTP_CLIENT = null;

    private HttpClientUtil() {
    }

    /**
     * 通过连接池获取HttpClient
     *
     * @return HttpClient
     */
    public static CloseableHttpClient getHttpClient() throws Exception {
        if (HTTP_CLIENT == null) {
            synchronized(syncLock) {
                if (HTTP_CLIENT == null) {
                    HTTP_CLIENT = createHttpClient();
               }
           }
        }

        return HTTP_CLIENT;
    }
    public static CloseableHttpClient createHttpClient() throws Exception {

        RequestConfig requestConfig = RequestConfig.custom()
                // 设置客户端和服务端建立连接的超时时间
                .setConnectTimeout(CONNECT_TIMEOUT)
                // 设置客户端从服务端读取数据的超时时间
                .setSocketTimeout(SOCKET_TIMEOUT)
                // 设置从连接池获取连接的超时时间，不宜过长
                .setConnectionRequestTimeout(REQUEST_TIMEOUT)
                .build();


        SSLContextBuilder builder = new SSLContextBuilder();
        // 全部信任 不做身份鉴定
        builder.loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                return true;
            }

        });
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE);
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register(HTTP, new PlainConnectionSocketFactory())
                .register(HTTPS, sslsf)
                .build();

        // Httpclient连接池，支持 http https
        PoolingHttpClientConnectionManager poolingConnectionManager =
                new PoolingHttpClientConnectionManager(registry );
        // 连接池最大连接数
        poolingConnectionManager.setMaxTotal(MAX_TOTAL);
        // 设置同路由的并发数
        poolingConnectionManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);
        // 可用空闲连接过期时间
        poolingConnectionManager.setValidateAfterInactivity(ValidateAfterInactivity);

        CloseableHttpClient httpClient = HttpClientBuilder.create()
                .setDefaultRequestConfig(requestConfig)
                .setConnectionManager(poolingConnectionManager)
                .build();
        return httpClient ;
    }
    public static String get(String url) throws Exception {
        return get(url, Collections.emptyMap(), Collections.emptyMap());
    }
 
    public static String get(String url, Map<String, Object> params) throws Exception {
        return get(url, Collections.emptyMap(), params);
    }
 
    public static String get(String url, Map<String, Object> headers, Map<String, Object> params)
            throws Exception {
        URIBuilder ub = new URIBuilder(url);
        if (MapUtils.isNotEmpty(params)) {
            ArrayList<NameValuePair> pairs = buildParameters(params);
            ub.setParameters(pairs);
        }
        HttpGet httpGet = new HttpGet(ub.build());
        if (MapUtils.isNotEmpty(headers)) {
            for (Map.Entry<String, Object> param : headers.entrySet()) {
                httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));
            }
        }
        return getResponse(httpGet);
    }
 
    public static String post(String url) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        return getResponse(httpPost);
    }
 
    public static String post(String url, String jsonStr) throws Exception {
        return post(url, Collections.emptyMap(), jsonStr);
    }
 
    public static String post(String url, Map<String, Object> headers, String jsonStr) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        if (MapUtils.isNotEmpty(headers)) {
            for (Map.Entry<String, Object> param : headers.entrySet()) {
                httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
            }
        }
        StringEntity entity = new StringEntity(jsonStr, StandardCharsets.UTF_8);
        entity.setContentEncoding(StandardCharsets.UTF_8.toString());
        entity.setContentType(JSON_CONTENT_TYPE);
        httpPost.setEntity(entity);
        return getResponse(httpPost);
    }
 
    public static String post(String url, Map<String, Object> params) throws Exception {
        HttpPost httpPost = new HttpPost(url);
        ArrayList<NameValuePair> pairs = buildParameters(params);
        httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8));
        return getResponse(httpPost);
    }
 
    public static String post(String url, Map<String, Object> headers, Map<String, Object> params)
            throws Exception {
        HttpPost httpPost = new HttpPost(url);
        if (MapUtils.isNotEmpty(headers)) {
            for (Map.Entry<String, Object> param : headers.entrySet()) {
                httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
            }
        }
 
        ArrayList<NameValuePair> pairs = buildParameters(params);
        httpPost.setEntity(new UrlEncodedFormEntity(pairs, StandardCharsets.UTF_8));
        return getResponse(httpPost);
    }
 
    private static ArrayList<NameValuePair> buildParameters(Map<String, Object> params) {
        ArrayList<NameValuePair> pairs = new ArrayList<>();
        for (Map.Entry<String, Object> param : params.entrySet()) {
            pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));
        }
        return pairs;
    }
 
    /**
     * 处理http请求
     *
     * @param request 请求参数
     * @return 响应结果
     * @throws Exception 异常
     */
    private static String getResponse(HttpRequestBase request) throws Exception {
        try (CloseableHttpResponse response = getHttpClient().execute(request)) {
            // 判断返回状态是否返回为200
            if (response != null && response.getStatusLine().getStatusCode() == SUCCESS_STATUS) {
                return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
            }
        }
        return null;
    }
 
}